use crate::{model};
use crate::model::state_code::{StateCode, StateCodeError};
use crate::model::user_token;
use futures::{future, TryFuture};
use http::header::HeaderValue;
use http::{HeaderMap, StatusCode};
use mongodb::bson::oid::ObjectId;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::convert::Infallible;
use std::net::SocketAddr;
use std::str::FromStr;
use warp::reply::{json, Json};
use warp::{Filter, Rejection, Reply};


pub struct Context {
    /// 会话Id
    token: String,
    /// 用户Id
    user_id: ObjectId,
    /// url参数
    query: HashMap<String, String>,
    /// form表单参数
    form: HashMap<String, String>,
    /// client ip
    ip: String,
}

impl Context {
    pub fn get_token(&self) -> &String {
        &self.token
    }

    pub fn get_user_id(&self) -> &ObjectId {
        &self.user_id
    }
    pub fn get_ip(&self) -> String {
        self.ip.clone()
    }

    /// 获取参数对象
    pub fn param<T: FromStr>(&self, param_name: &str) -> Result<T, Rejection> {
        let mut field_value = self.query.get(&param_name.to_string());
        if field_value.is_none() {
            field_value = self.form.get(&param_name.to_string());
        }

        return match field_value {
            Some(val) => {
                let result = val.parse::<T>();
                match result {
                    Ok(val) => Ok(val),
                    Err(_) => Err(warp::reject::custom(StateCodeError::from_code_str(
                        StateCode::ParamError,
                        param_name.to_string(),
                    ))),
                }
            }
            None => Err(warp::reject::custom(StateCodeError::from_code_str(
                StateCode::ParamError,
                param_name.to_string(),
            ))),
        };
    }

    /// 进行具体应答
    pub fn response<'a, TData: Serialize + Deserialize<'a>>(
        &self,
        code: StateCodeError,
        data: Option<&'a TData>,
    ) -> Result<Json, Rejection> {
        let response = model::Response::new(code.code() as i32, code.message(), data);
        Ok(json(&response))
    }

    pub fn response_code(&self, code: impl Into<StateCodeError>) -> Result<Json, Rejection> {
        self.response(code.into(), Option::<&i32>::None)
    }

    /// 真实IP过滤器
    fn remote_ip_filter() -> impl warp::Filter<Extract=(String, ), Error=Infallible> + Copy {
        warp::any()
            .and(warp::header("x-real-ip").map(|val: String| val.to_string()))
            .or(warp::header("x-forwarded-for").map(|val: String| val.to_string()))
            .unify()
            .or(
                warp::filters::addr::remote().map(|addr: Option<SocketAddr>| {
                    let mut ip: String = "".to_string();
                    match addr {
                        Some(val) => {
                            ip = val.ip().to_string();
                        }
                        _ => {}
                    }
                    ip
                }),
            )
            .unify()
    }

    pub fn api_detail<TResult, TOk>(
        other_filter: impl Filter<Extract=(), Error=Rejection> + Clone + Send,
        path: &'static str,
        is_need_login: bool,
        func: impl Fn(Context) -> TResult + Clone + Sized + Send,
    ) -> impl warp::Filter<Extract=(TOk, ), Error=Rejection> + Clone + Send
        where
            TResult: TryFuture<Ok=TOk, Error=Rejection> + Send,
            TOk: warp::Reply + Send,
    {
        let filter = warp::path(path)
            .and(other_filter)
            .and(warp::query())
            //.and(warp::body::content_length_limit(10240)) // 由于有些请求会报错，所以暂时先屏蔽掉
            .and(warp::body::form())
            .and(
                warp::any()
                    .and(warp::filters::header::header("token"))
                    .or(warp::any().map(|| "".to_string()))
                    .unify(),
            )
            .and(Self::remote_ip_filter())
            .and_then(
                move |query: HashMap<String, String>,
                      form: HashMap<String, String>,
                      token: String,
                      ip: String| {
                    if is_need_login == false {
                        return future::ok((Context {
                            user_id: mongo_util::zero_object_id(),
                            token: "".to_string(),
                            query,
                            form,
                            ip,
                        }, ));
                    }
                    if token.is_empty() {
                        return future::err(
                            warp::reject::custom(StateCode::UserNotLogin) as Rejection
                        );
                    }

                    let token_result = user_token::check_token(&token);
                    let token_obj;
                    match token_result {
                        Ok(Some(val)) => token_obj = val,
                        Ok(None) | Err(_) => {
                            return future::err(
                                warp::reject::custom(StateCode::UserNotLogin) as Rejection
                            );
                        }
                    }

                    let user_id = token_obj.read().unwrap().get_user_id();
                    return future::ok((Context {
                        user_id,
                        token,
                        query,
                        form,
                        ip,
                    }, ));
                },
            )
            .untuple_one()
            .and_then(func);

        return filter;
    }

    /// 获取一个基于Context的请求过滤器
    pub fn api<TResult, TOk>(
        other_filter: impl Filter<Extract=(), Error=Rejection> + Clone + Send,
        path: &'static str,
        func: impl Fn(Context) -> TResult + Clone + Sized + Send,
    ) -> impl warp::Filter<Extract=(TOk, ), Error=Rejection> + Clone + Send
        where
            TResult: TryFuture<Ok=TOk, Error=Rejection> + Send,
            TOk: warp::Reply + Send,
    {
        Context::api_detail(other_filter, path, false, func)
    }

    pub fn api_login<TResult, TOk>(
        other_filter: impl Filter<Extract=(), Error=Rejection> + Clone + Send,
        path: &'static str,
        func: impl Fn(Context) -> TResult + Clone + Sized + Send,
    ) -> impl warp::Filter<Extract=(TOk, ), Error=Rejection> + Clone + Send
        where
            TResult: TryFuture<Ok=TOk, Error=Rejection> + Send,
            TOk: warp::Reply + Send,
    {
        Context::api_detail(other_filter, path, true, func)
    }

    pub async fn handle_rejection(err: Rejection) -> Result<impl warp::Reply, Infallible> {
        return if err.is_not_found() {
            let code = StateCode::Error;
            let response =
                model::Response::<i32>::new(code as i32, code.message().to_string(), None);
            Ok(warp::reply::with_status(
                json(&response).into_response(),
                StatusCode::NOT_FOUND,
            ))
        } else if let Some(val) = err.find::<StateCode>() {
            let response =
                model::Response::<i32>::new(*val as i32, val.message().to_string(), None);
            let result = Ok(warp::reply::with_status(
                json(&response).into_response(),
                StatusCode::OK,
            ));
            result
        } else if let Some(val) = err.find::<StateCodeError>() {
            let response =
                model::Response::<i32>::new(val.code().code(), val.message().to_string(), None);
            Ok(warp::reply::with_status(
                json(&response).into_response(),
                StatusCode::OK,
            ))
        } else if err.is_not_found() {
            Ok(warp::reply::with_status(
                warp::reply().into_response(),
                StatusCode::NOT_FOUND,
            ))
        } else {
            eprintln!("unhandled error: {:?}", &err);
            let response = format!("{:?}", &err).into_response();
            Ok(warp::reply::with_status(
                response,
                StatusCode::INTERNAL_SERVER_ERROR,
            ))
        };
    }

    pub fn request_wrapper<F, T>(
        filter: F,
    ) -> impl Filter<Extract=(impl warp::Reply + Send, )> + Clone + Send + Sync + 'static
        where
            F: Filter<Extract=(T, ), Error=Infallible> + Clone + Send + Sync + 'static,
            F::Extract: warp::Reply,
            T: warp::Reply + Send,
    {
        let mut ext_headers = HeaderMap::new();
        ext_headers.insert("Access-Control-Allow-Origin", HeaderValue::from_static("*"));
        ext_headers.insert("Access-Control-Allow-Headers", HeaderValue::from_static("Origin,Authorization,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type,lang,token"));
        ext_headers.insert(
            "Access-Control-Allow-Methods",
            HeaderValue::from_static("POST, GET, OPTIONS, PUT, PATCH, DELETE"),
        );
        ext_headers.insert("Access-Control-Expose-Headers", HeaderValue::from_static("Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type, lang, token"));
        ext_headers.insert(
            "Access-Control-Allow-Credentials",
            HeaderValue::from_static("false"),
        );

        warp::any()
            .map(|| {
                //println!("before filter");
            })
            .untuple_one()
            .and(filter)
            //.map(|val| {
            // warp::reply::with_header(val,"h1","asdf").into_response()
            //    val
            //})
            .with(warp::reply::with::headers(ext_headers))
    }
}

unsafe impl Send for Context {}
